package com.hmdp.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.Result;
import com.hmdp.entity.Shop;
import com.hmdp.mapper.ShopMapper;
import com.hmdp.service.IShopService;
import com.hmdp.utils.CancheClient;
import com.hmdp.utils.RedisConstants;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private CancheClient cancheClient;

    /**
     * @param id
     * @return com.hmdp.entity.Shop
     * @description 查询商铺 函数调用
     * @author 31115
     * @date 2022/11/18 15:53
     */
    @Override
    public Result selectByid(Long id) {
        //解决穿透 雪崩问题
        /*Shop shop = cancheClient.getQueryWithPassThrough(RedisConstants.CACHE_SHOP_KEY + id,
                id,
                Shop.class,
                this::getById,
                RedisConstants.CACHE_SHOP_TTL,
                TimeUnit.MINUTES);*/

        //逻辑删除解决缓存击穿
        Shop shop = cancheClient.getQueryWithLogicalExpire(RedisConstants.CACHE_SHOP_KEY,
                id,
                Shop.class,
                this::getById,
                10L,
                TimeUnit.SECONDS);

        if (shop == null) {
            return Result.fail("店铺不存在");
        }
        return Result.ok(shop);
    }

    /**
     * @description 封装店铺查询 解决穿透
     * @param id
     * @return com.hmdp.entity.Shop
     * @author 31115
     * @date 2022/11/20 20:55
     */
   /* public Shop queryWithPassThrough(Long id){
        String key = RedisConstants.CACHE_SHOP_KEY + id;
        String shopJson = stringRedisTemplate.opsForValue().get(key);
        //使用redis查询数据
        if (StringUtils.isNotBlank(shopJson)) {
            //不为空 存在 直接返回
            return JSONUtil.toBean(shopJson, Shop.class);
        }

        //判断如果redis数据为空则提示用户
        if(shopJson != null){
            return null;
        }

        //尝试获取互斥锁
        String lockKey = "lockKey:shop:" + id;
        Shop shop = null;
        try {
        boolean lock = tryLock(lockKey);
        //判断锁是否获取成功
            if (!lock){
                //如果没有获取到休眠一段时间 继续重新查询
                Thread.sleep(50);
               return queryWithPassThrough(id);
            }
            //如果获取到了就走数据库重构缓存
            Thread.sleep(200);
            //如果不存在则查询数据库
            shop = getById(id);

            if(shop == null){

                //赋空值 (缓存穿透)
                stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id,"",
                        com.hmdp.utils.RedisConstants.CACHE_NULL_TTL,TimeUnit.MINUTES);

                return null;
            }

            //数据库查询出返回数据 并且存入redis
            Random random = new Random();
            int nextInt = random.nextInt(2) + 1;
            stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),
                    CACHE_SHOP_TTL + nextInt, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            //释放互斥锁
            unLock(lockKey);
        }

        return shop;
    }

    //线程池
    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);

    *//**
     * @description 逻辑过期 查询 解决击穿
     * @param id
     * @return com.hmdp.entity.Shop
     * @author 31115
     * @date 2022/11/20 23:22
     *//*
    public Shop queryWith(Long id){
        String key = RedisConstants.CACHE_SHOP_KEY + id;
        String shopJson = stringRedisTemplate.opsForValue().get(key);
        //使用redis查询数据
        if (StringUtils.isBlank(shopJson)) {
            //不存在 返回null
            return null;
        }

        //存在 先获取redis中的数据 转为对象
        RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);
        JSONObject shopData = (JSONObject) redisData.getData();
        Shop shop = JSONUtil.toBean(shopData, Shop.class);
        LocalDateTime expireTime = redisData.getExpireTime();
        //判断是否过期
        if (expireTime.isAfter(LocalDateTime.now())){
            //还没过期
            return shop;
        }

        //过期了
        //获取互斥锁
        String lockKey = com.hmdp.utils.RedisConstants.LOCK_SHOP_KEY + id;
        boolean lock = tryLock(lockKey);
        if(lock){
            try {
                //获取锁成功 开线程重建缓存
                CACHE_REBUILD_EXECUTOR.submit(new Runnable() {
                    @Override
                    public void run() {
                        saveShop2Redis(id,20L);
                    }
                });
            } catch (Exception e) {
                throw new RuntimeException(e);
            }finally {
                //释放锁
                unLock(lockKey);
            }
        }

        return shop;
    }

    *//**
     * @description 添加到redis （逻辑过期）
     * @param id
     * @param expireSeconds
     * @return void
     * @author 31115
     * @date 2022/11/20 23:10
     *//*
    public void saveShop2Redis(Long id,Long expireSeconds){
        //先根据id查出数据
        Shop shop = getById(id);
        //封装到redisData中
        RedisData redisData = new RedisData();
        redisData.setData(shop);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));
        //在添加到redis
        stringRedisTemplate.opsForValue().set(com.hmdp.utils.RedisConstants.CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(redisData));
    }

    *//**
     * @description 尝试获取锁 setnx
     * @param key
     * @return boolean
     * @author 31115
     * @date 2022/11/20 20:51
     *//*
    public boolean tryLock(String key){
        Boolean ifAbsent = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(ifAbsent);
    }

    *//**
     * @description 释放setnx锁
     * @param key
     * @return void
     * @author 31115
     * @date 2022/11/20 20:54
     *//*
    public void unLock(String key){
        stringRedisTemplate.delete(key);
    }*/

    /**
     * @param shop
     * @return void
     * @description 更新
     * @author 31115
     * @date 2022/11/20 14:46
     */
    @Override
    @Transactional
    public Result updateShopById(Shop shop) {
        Long id = shop.getId();
        if (id == null) {
            return Result.fail("id为空,不能更新");
        }
        //先更新数据库
        updateById(shop);
        //在删除缓存
        stringRedisTemplate.delete(com.hmdp.utils.RedisConstants.CACHE_SHOP_KEY + id);
        return Result.ok();
    }
}
